package domain;

import java.util.HashMap;
import java.util.HashSet;

import utils.exception.ConceptException;

import entities.concept.DomainConcept;
import entities.concept.DomainConceptEQ;
import entities.concept.DomainConceptLT;
import entities.role.Role;

@Deprecated
/**
 * An attempt to decide SAT of domain concepts using a watched literal like algorithm.
 * Notes:
 * 1. allowed predicates: = < <=
 * 2. LHS and RHS both allow roles and strings for any of the above predicates
 * 3. No functions (either interpreted or uninterpreted) allowed
 * @author j55wu
 *
 */
public class WatchedProcedure {
	private HashSet<DomainConcept<?, ?>> dcs;
	private HashMap<Role, HashSet<Object>> EQMAP;
	private HashMap<Role, HashSet<Object>> LTMAP;
	private boolean SAT;

	public WatchedProcedure(HashSet<DomainConcept<?, ?>> dcs) {
		this.dcs = dcs;
		this.EQMAP = new HashMap<Role, HashSet<Object>>();
		this.LTMAP = new HashMap<Role, HashSet<Object>>();
		this.SAT = true;
	}

	// build two maps of = and <
	public void preprocess() throws ConceptException {
		// HashSet<DomainConcept<?,?>> newdcs = new
		// HashSet<DomainConcept<?,?>>();
		for (DomainConcept<?, ?> dc : this.dcs) {
			if (dc instanceof DomainConceptEQ<?, ?>) {
				if (!preprocessDCEQ((DomainConceptEQ<?, ?>) dc) && !this.SAT) {
					return;
				}
			} else if (dc instanceof DomainConceptLT<?, ?>) {
				preprocessDCLT((DomainConceptLT<?, ?>) dc);
			}
		}
	}

	private boolean preprocessDCLT(DomainConceptLT<?, ?> dclt)
			throws ConceptException {
		Object lhs = dclt.getLHS();
		Object rhs = dclt.getValue();
		if (lhs instanceof Role) {
			return this.addRoleToLTMap((Role) lhs, rhs);
		} else if (rhs instanceof Role) {
			return this.addRoleToLTMap((Role) rhs, lhs);
		} else {
			// two strings
			if (!(((String) lhs).compareTo((String) rhs) < 0)) {
				this.SAT = false;
			}
			// whether c1 < c2 or not, return false as this is not needed
			return false;
		}
	}

	private boolean addRoleToLTMap(Role f, Object v) throws ConceptException {
		if (v instanceof String) {
			return this.addRSToMap(f, (String) v, FeatureGraph.INTLT);
		} else if (v instanceof Role) {
			return this.addRRToMap(f, (Role) v, FeatureGraph.INTLT);
		} else {
			throw new ConceptException("Unsupported domain concept: " + f
					+ " = " + v);
		}
	}

	// return false if no need to add OR unsat value found, true otherwise.
	private boolean preprocessDCEQ(DomainConceptEQ<?, ?> dce)
			throws ConceptException {
		Object lhs = dce.getLHS();
		Object rhs = dce.getValue();
		if (lhs instanceof Role) {
			return this.addRoleToEQMap((Role) lhs, rhs);
		} else if (rhs instanceof Role) {
			return this.addRoleToEQMap((Role) rhs, lhs);
		} else {
			// two strings only
			if (((String) lhs).compareTo((String) rhs) != 0) {
				this.SAT = false;
			}
			// whether c1=c2 or c1!=c2, return false as this is not needed
			return false;
		}
	}

	private boolean addRoleToEQMap(Role f, Object g) throws ConceptException {
		if (g instanceof String) {
			return this.SAT
					&& this.addRSToMap(f, (String) g, FeatureGraph.INTEQ);
		} else if (g instanceof Role) {
			// f=g. if f has a value c, add g=c, no need to add this cocnept
			// if f has no value, add g to f={g}
			return this.SAT && this.addRRToMap(f, (Role) g, FeatureGraph.INTEQ);
		} else {
			throw new ConceptException("Unsupported domain concept: " + f
					+ " = " + g);
		}
	}

	// return false if no need to add OR unsat value found, true otherwise.
	private boolean addRSToMap(Role f, String v, int type) {
		if (type == FeatureGraph.INTEQ) {
			if (this.EQMAP.containsKey(f)) {
				boolean add = true;
				for (Object vv : this.EQMAP.get(f)) {
					if (vv instanceof String) {
						int comp = ((String) vv).compareTo(v);
						if (comp == 0) {
							// no need to add
							add = false;
							break;
						} else {
							// inconsistency
							this.SAT = false;
							return false;
						}
					}
				}
				if (add) {
					this.EQMAP.get(f).add(v);
				}
			} else {
				// add directly
				HashSet<Object> vs = new HashSet<Object>();
				vs.add(v);
				this.EQMAP.put(f, vs);
			}
		} else if (type == FeatureGraph.INTLT) {
			// f<c, but f already has a smaller value, quit
			if (this.LTMAP.containsKey(f)) {
				boolean add = true;
				for (Object vv : this.LTMAP.get(f)) {
					if (vv instanceof String) {
						int comp = ((String) vv).compareTo(v);
						if (comp <= 0) {
							// no need to add
							add = false;
							break;
						} else {
							// this v is a smaller value; add it here
							this.LTMAP.get(f).remove(vv);
							this.LTMAP.get(f).add(v);
							// TODO remove all values and re-add those smaller
							// than v?
							return true;
						}
					}
					// what is f<g? add it..
				}
				if (add) {
					this.LTMAP.get(f).add(v);
				}
			} else {
				// add directly
				HashSet<Object> vs = new HashSet<Object>();
				vs.add(v);
				this.LTMAP.put(f, vs);
			}
		}
		return true;
	}

	// return false if no need to add OR unsat value found, true otherwise.
	private boolean addRRToMap(Role f, Role g, int type) {
		if (type == FeatureGraph.INTEQ) {
			if (this.EQMAP.containsKey(f)) {
				for (Object vv : this.EQMAP.get(f)) {
					if (vv instanceof String) {
						// f=g /\ f=c ==> g=c
						return this.addRSToMap(g, (String) vv,
								FeatureGraph.INTEQ);
					} else {
						// f=g added
						this.EQMAP.get(f).add(g);
						return true;
					}
				}
			} else {
				// add directly
				HashSet<Object> vs = new HashSet<Object>();
				vs.add(g);
				this.EQMAP.put(f, vs);
			}
		} else if (type == FeatureGraph.INTLT) {
			// add directly
			HashSet<Object> vs = new HashSet<Object>();
			vs.add(g);
			this.EQMAP.put(f, vs);
		}
		return true;
	}

}
